package com.cocotingo.snail.template.parser;

import java.awt.Font;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.cocotingo.snail.template.TemplateParserException;
import com.cocotingo.snail.template.models.Attribute;
import com.cocotingo.snail.template.models.HandlerNode;
import com.cocotingo.snail.template.models.LayoutFactorNode;
import com.cocotingo.snail.template.models.LayoutNode;
import com.cocotingo.snail.template.models.PaintFactorNode;
import com.cocotingo.snail.template.models.PaintFactorNode.PaintImageNode;
import com.cocotingo.snail.template.models.ResourceNode.ClassNode;
import com.cocotingo.snail.template.models.PaintNode;
import com.cocotingo.snail.template.models.PreviewNode;
import com.cocotingo.snail.template.models.ResourceNode;
import com.cocotingo.snail.template.models.ResourcesNode;
import com.cocotingo.snail.template.models.TemplateDefinitionsNode;
import com.cocotingo.snail.template.models.TemplateNode;
import com.cocotingo.snail.template.models.VariableNode;
import com.cocotingo.snail.template.models.VariablesNode;
import com.cocotingo.snail.template.models.ViewNode;

public class XMLTemplateParser {
	
	public static final String VERSION = "2.0";

	private DocumentBuilderFactory builderFactory;
	private DocumentBuilder builder;

	public XMLTemplateParser() {
		builderFactory = DocumentBuilderFactory.newInstance();
		builderFactory.setIgnoringComments(true);
		builderFactory.setIgnoringElementContentWhitespace(true);
	}

	public TemplateDefinitionsNode parse(InputStream xml)
			throws TemplateParserException {
		try {
			builder = builderFactory.newDocumentBuilder();
			Document doc = builder.parse(xml);
			return createTemplateDefinitionsNode(doc);
		} catch (ParserConfigurationException e) {
			throw new TemplateParserException(e);
		} catch (SAXException e) {
			throw new TemplateParserException(e);
		} catch (IOException e) {
			throw new TemplateParserException(e);
		}
	}

	private TemplateDefinitionsNode createTemplateDefinitionsNode(Document doc) throws TemplateParserException {
		NodeList nodes = doc.getChildNodes();
		if (nodes.getLength() == 0) throw new TemplateParserException("missing TemplateDefinitions node");
		Node node = nodes.item(0);
		if (node.getNodeType() == Node.ELEMENT_NODE
			&& node.getNodeName().equals("TemplateDefinitions")) {
			TemplateDefinitionsNode definitions = new TemplateDefinitionsNode();
			NamedNodeMap attrs = node.getAttributes();
			if (attrs.getLength() == 0) throw new TemplateParserException("missing version attribute on TemplateDefinitions node");
			Node attr = attrs.item(0);
			if (attr.getNodeName().equals("version")) {
				definitions.setVersion(attr.getNodeValue());
				if (!VERSION.equals(definitions.getVersion())) {
					throw new TemplateParserException("incorrect TemplateDefinitions version: " + definitions.getVersion());
				}
				createTemplateDefinitionsChildren(definitions, node.getChildNodes());
				return definitions;
			} else {
				throw new TemplateParserException("unknown attribute on TemplateDefinitions node: " + attr);
			}
		} else {
			throw new TemplateParserException("unknown node: " + node);
		}
	}
	
	private void createTemplateDefinitionsChildren(TemplateDefinitionsNode definitions, NodeList children) throws TemplateParserException {
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				String nodeName = node.getNodeName();
				if (nodeName.equals("Resources")) {
					definitions.addResourcesNode(createResourcesNode(node));
				} else if (nodeName.equals("Template")) {
					definitions.addTemplateNode(createTemplateNode(node));
				} else {
					throw new TemplateParserException("unknown node: " + node);
				}
			}
		}
	}
	
	private ResourcesNode createResourcesNode(Node node) throws TemplateParserException {
		ResourcesNode resources = new ResourcesNode();
		HashMap<String, String> valueMap = new HashMap<String, String>(0);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Resources");
		NodeList children = node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node child = children.item(i);
			if (child.getNodeType() == Node.ELEMENT_NODE) {
				String nodeName = child.getNodeName();
				if (nodeName.equals("Class")) {
					resources.addResourceNode(createClassNode(child));
				} else if (nodeName.equals("Image")) {
					resources.addResourceNode(createImageNode(child));
				} else if (nodeName.equals("ResizableImage")) {
					resources.addResourceNode(createResizableImageNode(child));
				} else if (nodeName.equals("Color")) {
					resources.addResourceNode(createColorNode(child));
				} else if (nodeName.equals("Font")) {
					resources.addResourceNode(createFontNode(child));
				} else {
					throw new TemplateParserException("unknown node: " + child);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(child)) {
				throw new TemplateParserException("unknown node: " + child);
			}
		}
		return resources;
	}
	
	private ClassNode createClassNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(1);
		valueMap.put("id", null);
		valueMap.put("name", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Class");
		ClassNode classNode = new ClassNode();
		classNode.setID(valueMap.get("id"));
		classNode.setClassName(valueMap.get("name"));
		ParserUtility.ensureNoChildrenOnNode(node, "Class");
		return classNode;
	}
	
	private ResourceNode createImageNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(2);
		valueMap.put("id", null);
		valueMap.put("resource", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Image");
		ResourceNode.ImageNode imgNode = new ResourceNode.ImageNode();
		imgNode.setID(valueMap.get("id"));
		imgNode.setResourceName(valueMap.get("resource"));
		ParserUtility.ensureNoChildrenOnNode(node, "Image");
		return imgNode;
	}
	
	private ResourceNode createResizableImageNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(3);
		valueMap.put("id", null);
		valueMap.put("anchors", null);
		valueMap.put("resource", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "ResizableImage");
		ResourceNode.ResizableImageNode imgNode = new ResourceNode.ResizableImageNode();
		imgNode.setID(valueMap.get("id"));
		imgNode.setResourceName(valueMap.get("resource"));
		int[] anchors = ParserUtility.getAnchorValuesFromString(valueMap.get("anchors"));
		imgNode.setAnchors(anchors[0], anchors[1], anchors[2], anchors[3]);
		ParserUtility.ensureNoChildrenOnNode(node, "ResizableImage");
		return imgNode;
	}
	
	private ResourceNode createColorNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(2);
		valueMap.put("id", null);
		valueMap.put("rgb", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Color");
		ResourceNode.ColorNode colorNode = new ResourceNode.ColorNode();
		colorNode.setID(valueMap.get("id"));
		colorNode.setColor(ParserUtility.getColorValueFromString(valueMap.get("rgb")));
		ParserUtility.ensureNoChildrenOnNode(node, "Color");
		return colorNode;
	}
	
	private ResourceNode createFontNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(4);
		valueMap.put("id", null);
		valueMap.put("name", null);
		valueMap.put("style", null);
		valueMap.put("size", null);
		ParserUtility.getOptionalAttributesFromNodeStrictly(node, valueMap, "Font");
		ResourceNode.FontNode fontNode = new ResourceNode.FontNode();
		String id = valueMap.get("id");
		if (id == null) throw new TemplateParserException("missing id attribute on Font node");
		fontNode.setID(id);
		String size = valueMap.get("size");
		if (size == null) throw new TemplateParserException("missing size attribute on Font node");
		try {
			int s = Integer.parseInt(size);
			if (s <= 0) throw new TemplateParserException("invalid font size: " + s);
			fontNode.setFontSize(s);
		} catch (NumberFormatException e) {
			throw new TemplateParserException("incorrect font size format: " + size, e);
		}
		String style = valueMap.get("style");
		if (style != null) {
			if (style.equals("plain")) {
				fontNode.setFontStyle(Font.PLAIN);
			} else if (style.equals("bold")) {
				fontNode.setFontStyle(Font.BOLD);
			} else if (style.equals("italic")) {
				fontNode.setFontStyle(Font.ITALIC);
			} else if (style.equals("bold+italic")) {
				fontNode.setFontStyle(Font.BOLD + Font.ITALIC);
			} else {
				throw new TemplateParserException("invalid font style: " + valueMap.get("style"));
			}
		}
		String name = valueMap.get("name");
		if (name != null) fontNode.setFontName(name);
		ParserUtility.ensureNoChildrenOnNode(node, "Color");
		return fontNode;
	}
	
	private TemplateNode createTemplateNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(1);
		valueMap.put("id", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Template");
		TemplateNode template = new TemplateNode();
		template.setID(valueMap.get("id"));
		NodeList nodes = node.getChildNodes();
		Node varsNode = null;
		Node viewNode = null;
		for (int i = 0; i < nodes.getLength(); i++) {
			Node n = nodes.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("View")) {
					if (viewNode == null) {
						viewNode = n;
					} else {
						throw new TemplateParserException("duplicate root View node on Template node");
					}
				} else if (n.getNodeName().equals("Variables")) {
					if (varsNode == null) {
						varsNode = n;
					} else {
						throw new TemplateParserException("duplicate Variables node on Template node");
					}
				} else {
					throw new TemplateParserException("unknown node on Template node: " + n);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(n)) {
				throw new TemplateParserException("unknown node on Template node: " + n);
			}
		}
		if (varsNode != null) template.setVariablesNode(createVariablesNode(varsNode));
		if (viewNode == null) throw new TemplateParserException("missing root View node on Template node");
		template.setRootViewNode(createViewNode(viewNode));
		return template;
	}
	
	private VariablesNode createVariablesNode(Node node) throws TemplateParserException {
		VariablesNode varsNode = new VariablesNode();
		ParserUtility.ensureNoAttributesOnNode(node, "Variables");
		NodeList nodes = node.getChildNodes();
		for (int i = 0; i < nodes.getLength(); i++) {
			Node n = nodes.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("Integer")) {
					varsNode.addVariableNode(createIntegerNode(n));
				} else if (n.getNodeName().equals("Double")) {
					varsNode.addVariableNode(createDoubleNode(n));
				} else {
					throw new TemplateParserException("unknown node on Variables node: " + n);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(n)) {
				throw new TemplateParserException("unknown node on Variables node: " + n);
			}
		}
		return varsNode;
	}
	
	private VariableNode<?> createIntegerNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(2);
		valueMap.put("id", null);
		valueMap.put("default", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Integer");
		VariableNode.IntegerNode intNode = new VariableNode.IntegerNode();
		intNode.setID(valueMap.get("id"));
		try {
			int defaultValue = Integer.parseInt(valueMap.get("default"));
			intNode.setDefaultValue(defaultValue);
		} catch (NumberFormatException e) {
			throw new TemplateParserException("incorrect default value format on Integer node: " + valueMap.get("default"), e);
		}
		ParserUtility.ensureNoChildrenOnNode(node, "Integer");
		return intNode;
	}
	
	private VariableNode<?> createDoubleNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(2);
		valueMap.put("id", null);
		valueMap.put("default", null);
		ParserUtility.getRequiredAttributesFromNodeStrictly(node, valueMap, "Double");
		VariableNode.DoubleNode doubleNode = new VariableNode.DoubleNode();
		doubleNode.setID(valueMap.get("id"));
		try {
			double defaultValue = Double.parseDouble(valueMap.get("default"));
			doubleNode.setDefaultValue(defaultValue);
		} catch (NumberFormatException e) {
			throw new TemplateParserException("incorrect default value format on Double node: " + valueMap.get("default"), e);
		}
		ParserUtility.ensureNoChildrenOnNode(node, "Double");
		return doubleNode;
	}
	
	private ViewNode createViewNode(Node node) throws TemplateParserException {
		ViewNode viewNode = new ViewNode();
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			Node attr = attrs.item(i);
			if (attr.getNodeName().equals("id")) {
				viewNode.setID(attr.getNodeValue());
			} else if (attr.getNodeName().equals("class")) {
				viewNode.setClassName(attr.getNodeValue());
			} else if (attr.getNodeName().equals("template")) {
				viewNode.setTemplateName(attr.getNodeValue());
			} else {
				Attribute attribute = new Attribute(attr.getNodeName());
				attribute.setValue(attr.getNodeValue());
				viewNode.addAttribute(attribute);
			}
		}
		NodeList children = node.getChildNodes();
		createViewNodeChildren(children, viewNode);
		return viewNode;
	}
	
	private void createViewNodeChildren(NodeList children, ViewNode viewNode) throws TemplateParserException {
		Node layoutNode = null;
		Node paintNode = null;
		Node previewNode = null;
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				if (node.getNodeName().equals("View")) {
					viewNode.addViewNode(createViewNode(node));
				} else if (node.getNodeName().equals("Handler")) {
					viewNode.setHandlerNode(createHandlerNode(node));
				} else if (node.getNodeName().equals("Layout")) {
					if (layoutNode == null) layoutNode = node;
					else throw new TemplateParserException("duplicate Handler node on View node");
				} else if (node.getNodeName().equals("Paint")) {
					if (paintNode == null) paintNode = node;
					else throw new TemplateParserException("duplicate Paint node on View node");
				} else if (node.getNodeName().equals("Preview")) {
					if (previewNode == null) previewNode = node;
					else throw new TemplateParserException("duplicate Preview node on View node");
				} else {
					throw new TemplateParserException("unknown node on View node: " + node);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(node)) {
				throw new TemplateParserException("unknown node on View node: " + node);
			}
		}
		if (layoutNode != null) {
			viewNode.setLayoutNode(createLayoutNode(layoutNode));
		}
		if (paintNode != null) {
			viewNode.setPaintNode(createPaintNode(paintNode));
		}
		if (previewNode != null) {
			viewNode.setPreviewNode(createPreviewNode(paintNode));
		}
	}
	
	private HandlerNode createHandlerNode(Node node) throws TemplateParserException {
		HandlerNode handlerNode = new HandlerNode();
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			Node attr = attrs.item(i);
			handlerNode.addHandler(attr.getNodeName(), attr.getNodeValue());
		}
		ParserUtility.ensureNoChildrenOnNode(node, "Handler");
		return handlerNode;
	}
	
	private PaintNode createPaintNode(Node node) throws TemplateParserException {
		ParserUtility.ensureNoAttributesOnNode(node, "Paint");
		PaintNode paintNode = new PaintNode();
		NodeList children = node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node n = children.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("PaintImage")) {
					paintNode.addPaintFactor(createPaintImageNode(n));
				} else {
					throw new TemplateParserException("unknown node on Paint node: " + n);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(n)) {
				throw new TemplateParserException("unknown node on Paint node: " + n);
			}
		}
		return paintNode;
	}
	
	private PreviewNode createPreviewNode(Node node) throws TemplateParserException {
		ParserUtility.ensureNoAttributesOnNode(node, "Preview");
		PreviewNode paintNode = new PreviewNode();
		NodeList children = node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node n = children.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("PaintImage")) {
					paintNode.addPaintFactor(createPaintImageNode(n));
				} else {
					throw new TemplateParserException("unknown node on Preview node: " + n);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(n)) {
				throw new TemplateParserException("unknown node on Preview node: " + n);
			}
		}
		return paintNode;
	}
	
	private PaintFactorNode createPaintImageNode(Node node) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(2);
		valueMap.put("image", null);
		valueMap.put("style", null);
		ParserUtility.getOptionalAttributesFromNodeStrictly(node, valueMap, "PaintImage");
		PaintFactorNode.PaintImageNode paintImageNode = new PaintFactorNode.PaintImageNode();
		String id = valueMap.get("image");
		String style = valueMap.get("style");
		if (id != null) {
			paintImageNode.setImageID(id);
		} else {
			throw new TemplateParserException("missing id attribute on PaintImage node");
		}
		if (style != null) {
			if (style.equals("fill")) {
				paintImageNode.setStyle(PaintImageNode.FILL);
			} else if (style.equals("tile")) {
				paintImageNode.setStyle(PaintImageNode.TILE);
			} else if (style.equals("center")) {
				paintImageNode.setStyle(PaintImageNode.CENTER);
			} else {
				throw new TemplateParserException("invalid style: " + valueMap.get("style"));
			}
		}
		return paintImageNode;
	}

	private LayoutNode createLayoutNode(Node node) throws TemplateParserException {
		ParserUtility.ensureNoAttributesOnNode(node, "Layout");
		LayoutNode layoutNode = new LayoutNode();
		NodeList nodes = node.getChildNodes();
		for (int i = 0; i < nodes.getLength(); i++) {
			Node n = nodes.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("Left")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.LEFT));
				} else if (n.getNodeName().equals("CenterLeft")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.CENTER_LEFT));
				} else if (n.getNodeName().equals("Right")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.RIGHT));
				} else if (n.getNodeName().equals("Width")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.WIDTH));
				} else if (n.getNodeName().equals("Top")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.TOP));
				} else if (n.getNodeName().equals("CenterTop")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.CENTER_TOP));
				} else if (n.getNodeName().equals("Bottom")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.BOTTOM));
				} else if (n.getNodeName().equals("Height")) {
					layoutNode.addLayoutFactor(createNormalLayoutFactorNode(n, LayoutFactorNode.HEIGHT));
				} else if (n.getNodeName().equals("PreferredWidth")) {
					layoutNode.addLayoutFactor(createPreferredLayoutFactorNode(n, LayoutFactorNode.WIDTH));
				} else if (n.getNodeName().equals("PreferredHeight")) {
					layoutNode.addLayoutFactor(createPreferredLayoutFactorNode(n, LayoutFactorNode.HEIGHT));
				} else if (n.getNodeName().equals("HorizontalCenter")) {
					layoutNode.addLayoutFactor(createCenterredLayoutFactorNode(n, LayoutFactorNode.CENTER_LEFT));
				} else if (n.getNodeName().equals("VerticalCenter")) {
					layoutNode.addLayoutFactor(createCenterredLayoutFactorNode(n, LayoutFactorNode.CENTER_TOP));
				} else {
					throw new TemplateParserException("unknown node on Layout node: " + n);
				}
			} else if (!ParserUtility.isWhitespacedTextNode(n)) {
				throw new TemplateParserException("unknown node on Layout node: " + n);
			}
		}
		return layoutNode;
	}
	
	private LayoutFactorNode createPreferredLayoutFactorNode(Node node, int factorType) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(1);
		valueMap.put("default", null);
		ParserUtility.getOptionalAttributesFromNodeStrictly(node, valueMap, "LayoutFactor");
		ParserUtility.ensureNoChildrenOnNode(node, "LayoutFactor");
		String default_value = valueMap.get("default");
		LayoutFactorNode.PreferredLayoutFactorNode rst = new LayoutFactorNode.PreferredLayoutFactorNode();
		rst.setFactorType(factorType);
		if (default_value != null) {
			try {
				rst.setDefaultValue(Integer.parseInt(default_value));
			} catch (NumberFormatException e) {
				throw new TemplateParserException("incorrect integer format: " + default_value, e);
			}
		}
		return rst;
	}
	
	private LayoutFactorNode createCenterredLayoutFactorNode(Node node, int factorType) throws TemplateParserException {
		ParserUtility.ensureNoAttributesOnNode(node, "LayoutFactor");
		ParserUtility.ensureNoChildrenOnNode(node, "LayoutFactor");
		LayoutFactorNode.CenteredLayoutFactorNode rst = new LayoutFactorNode.CenteredLayoutFactorNode();
		rst.setFactorType(factorType);
		return rst;
	}

	private LayoutFactorNode createNormalLayoutFactorNode(Node node, int factorType) throws TemplateParserException {
		HashMap<String, String> valueMap = new HashMap<String, String>(6);
		valueMap.put("value", null);
		valueMap.put("reference", null);
		valueMap.put("multiplier", null);
		valueMap.put("neighbor", null);
		valueMap.put("margin", null);
		valueMap.put("content", null);
		ParserUtility.getOptionalAttributesFromNodeStrictly(node, valueMap, "LayoutFactor");
		ParserUtility.ensureNoChildrenOnNode(node, "LayoutFactor");
		String value = valueMap.get("value");
		String reference = valueMap.get("reference");
		String multiplier = valueMap.get("multiplier");
		String neighbor = valueMap.get("neighbor");
		String margin = valueMap.get("margin");
		String content = valueMap.get("content");
		
		if (value != null) {
			LayoutFactorNode.AbsoluteLayoutFactorNode rst = new LayoutFactorNode.AbsoluteLayoutFactorNode();
			rst.setFactorType(factorType);
			rst.setValue(value);
			if (reference != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: reference");
			if (multiplier != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: multiplier");
			if (neighbor != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: neighbor");
			if (margin != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: margin");
			if (content != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: content");
			return rst;
		} else if (reference != null) {
			LayoutFactorNode.ReferencedLayoutFactorNode rst = new LayoutFactorNode.ReferencedLayoutFactorNode();
			rst.setFactorType(factorType);
			rst.setReferencedView(reference);
			if (multiplier != null) rst.setMultiplier(multiplier);
			if (neighbor != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: neighbor");
			if (margin != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: margin");
			if (content != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: content");
			return rst;
		} else if (neighbor != null) {
			LayoutFactorNode.NeighborLayoutFactorNode rst = new LayoutFactorNode.NeighborLayoutFactorNode();
			rst.setFactorType(factorType);
			rst.setNeighborView(neighbor);
			if (margin != null) rst.setMargin(margin);
			if (multiplier != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: multiplier");
			if (content != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: content");
			return rst;
		} else if (content != null) {
			LayoutFactorNode.FittingLayoutFactorNode rst = new LayoutFactorNode.FittingLayoutFactorNode();
			rst.setFactorType(factorType);
			rst.setContentView(content);
			if (margin != null) rst.setMargin(margin);
			if (multiplier != null) throw new TemplateParserException("attribute conflict on LayoutFactor node: multiplier");
			return rst;
		} else {
			throw new TemplateParserException("missing attributes on LayoutFactor node");
		}
	}

}
